package com.zhan.easyexcelorder.service.impl;

import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import com.zhan.easyexcelorder.model.SysDepartment;
import com.zhan.easyexcelorder.model.SysRole;
import com.zhan.easyexcelorder.model.SysUser;
import com.zhan.easyexcelorder.repository.SysUserRepository;
import com.zhan.easyexcelorder.service.SysUserService;
import com.zhan.easyexcelorder.vo.AddressBookUserVO;
import com.zhan.easyexcelorder.vo.AddressBookVO;
import com.zhan.easyexcelorder.vo.SysUserVO;
import com.zhan.jpa.service.impl.AbstractServiceImpl;
import com.zhan.utils.Constants;
import com.zhan.utils.jwt.JwtUtils;
import com.zhan.utils.page.QueryCondition;
import com.zhan.utils.page.QueryItem;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.persistence.Query;
import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysUserServiceImpl extends AbstractServiceImpl<SysUserRepository, SysUser> implements SysUserService {

    private final static String DEFAULT_PASSWORD = "123456";

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Value(value = "${spring.application.name}")
    private String serverName;

    @Override
    protected void createPredicate(Root<SysUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder, List<Predicate> list, QueryCondition condition) {
        List<QueryItem> query = condition.getQuery();
        if (Objects.nonNull(query)) {
            for (QueryItem q : query) {
                String column = q.getColumn();
                Object value = q.getValue();
                String temp = null;
                if (Objects.nonNull(value)) {
                    if ("username".equals(column) && StringUtils.isNotBlank(value.toString())) {
                        temp = "%" + value.toString() + "%";
                        list.add(criteriaBuilder.or(criteriaBuilder.like(root.get("username").as(String.class), temp)));
                    } else if ("phone".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                        list.add(criteriaBuilder.equal(root.get("phone").as(String.class), temp));
                    } else if ("role".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                        Long roleId = Long.parseLong(temp);
                        ListJoin<SysUser, SysRole> join = root.join(root.getModel().getList("roles", SysRole.class));
                        list.add(criteriaBuilder.equal(join.get("id").as(Long.class), roleId));
                    } else if ("department".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                        Long departmentId = Long.parseLong(temp);
                        SysDepartment sysDepartment = new SysDepartment();
                        sysDepartment.setId(departmentId);
                        list.add(criteriaBuilder.equal(root.get("department").as(SysDepartment.class), sysDepartment));
                    } else if ("id".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                        Long id = Long.parseLong(temp);
                        list.add(criteriaBuilder.equal(root.get("id").as(Long.class), id));
                    }
                }
            }
        }
    }

    @Override
    public List<SysUser> findSysUserByRoleId(Long roleId) {
        //查询条件
        Specification<SysUser> spec = (Specification<SysUser>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> list = super.addDelStatus(root, criteriaQuery, criteriaBuilder);
            ListJoin<SysUser, SysRole> join = root.join(root.getModel().getList("roles", SysRole.class));
            list.add(criteriaBuilder.equal(join.get("id").as(Long.class), roleId));
            Predicate[] arr = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(arr));
        };
        return repository.findAll(spec);
    }

    @Transactional
    @Override
    public SysUser loginSelect(String username) throws Exception {
        SysUser sysUser = repository.loginSelect(username, Constants.NOT_DEL_VALUE);
        if (Objects.isNull(sysUser)) {
            throw new Exception("用户不存在或密码错误");
        }
        Set<SysRole> roles = sysUser.getRoles();
        if (Objects.nonNull(roles) && !roles.isEmpty()) {
            SysRole sysRole = roles.iterator().next();
            String hql = "select distinct m.operationCode from SysRole r join r.sysMenus m where r.id=" + sysRole.getId() + " and m.operationCode is not null";
            List<String> resultList = entityManager.createQuery(hql).getResultList();
            sysUser.setOperationCodeList(resultList);
        }
        sysUser.setLoginTime(new Date());
        repository.save(sysUser);
        return sysUser;
    }

    @Override
    public SysUser deleteUserRoles(SysUser sysUser) throws Exception {
        SysUser oldSysUser = repository.findById(sysUser.getId()).get();
        Set<SysRole> oldSysRoles = oldSysUser.getRoles();
        Set<SysRole> sysRoles = sysUser.getRoles();
        if (Objects.isNull(oldSysRoles) || oldSysRoles.isEmpty() || Objects.isNull(sysRoles) || sysRoles.isEmpty()) {
            throw new Exception("删除失败");
        }

        // 求差集
        Set<Long> oldRoleIds = new HashSet<>();
        Set<Long> roleIds = new HashSet<>();
        oldSysRoles.forEach((data) -> oldRoleIds.add(data.getId()));
        sysRoles.forEach((data) -> roleIds.add(data.getId()));
        Sets.SetView<Long> difference = Sets.difference(oldRoleIds, roleIds);

        // 更新
        Set<SysRole> result = changeIdsToSysRoles(difference);
        oldSysUser.setRoles(result);
        return repository.save(oldSysUser);
    }

    @Transactional
    @Override
    public Integer changePassword(String ids, String oldPassword, String newPassword) throws Exception {
        // 如果是重置密码
        if (StringUtils.isBlank(newPassword) || StringUtils.isBlank(oldPassword)) {
            newPassword = DEFAULT_PASSWORD;
            return repository.changePassword(Splitter.on(",").trimResults().splitToList(ids).stream().map(id -> Long.parseLong(id)).collect(Collectors.toList()), passwordEncoder.encode(newPassword), Constants.Login.PASSWORD_NOT_CHANGE, new Date(), jwtUtils.getUserId());
        } else {
            Long id = Long.parseLong(ids);
            String password = repository.findPasswordById(id);
            if (!passwordEncoder.matches(oldPassword, password)) {
                throw new Exception("原密码不正确！");
            }
            return repository.changePassword(Arrays.asList(id), passwordEncoder.encode(newPassword), Constants.Login.PASSWORD_CHANGED, new Date(), jwtUtils.getUserId());
        }
    }

    /**
     * 查询有请假审批权限的用户
     *
     * @param userId
     * @return
     */
    @Transactional
    @Override
    public List<SysUser> findSysUserByApproval(Long userId) {
        SysUser sysUser = repository.findById(userId).get();

        List<Long> departmentIds = new ArrayList<>();
        SysDepartment department = sysUser.getDepartment();
        departmentIds.add(department.getId());
        addParentDepartmentId(departmentIds, department);
        if (departmentIds.isEmpty()) {
            return Collections.EMPTY_LIST;
        }

        return repository.findSysUserByApproval("vacationApproval", departmentIds);
    }

    @Transactional
    @Override
    public List<AddressBookVO> findAddressBook(Long userId) {
        SysUser temp = repository.findById(userId).get();
        SysDepartment department = temp.getDepartment();
        List<Long> departmentIds = new ArrayList<>();
        addDepartmentId(departmentIds, department);
        List<SysUserVO> sysUserList = repository.findAllByDepartment(departmentIds);
        List<AddressBookVO> result = new ArrayList<>();

        addCommonAddressData(result);

        sysUserList.forEach(sysUser -> {
            AddressBookVO addressBookVO = new AddressBookVO();

            addressBookVO.setId(sysUser.getRoleId());
            addressBookVO.setType(sysUser.getRoleName());
            addressBookVO.setList(new ArrayList<>());

            int index = -1;
            if ((index = result.indexOf(addressBookVO)) != -1) {
                AddressBookVO t = result.get(index);
                addAddressBookUserVo(sysUser, t);
            } else {
                result.add(addressBookVO);
                addAddressBookUserVo(sysUser, addressBookVO);
            }
        });
        Collections.sort(result);
        return result;
    }

    @Override
    public Set<Long> getUserIds() {
        return repository.getUserIds();
    }

    @Transactional
    @Override
    public Integer changeDeviceToken(String deviceToken, Integer deviceType, Long userId) {
        return repository.changeDeviceToken(deviceToken, deviceType, new Date(), userId);
    }

    @Override
    public List<SysUserVO> getAll(QueryCondition condition) {
        StringBuffer hql = new StringBuffer();
        hql.append("select new cn.com.chinatelecom.callcenter.user.manage.vo.SysUserVO(u.id ,u.username ,u.phone,u.generalCode ,u.snatchCode ,u.department.id ,u.department.deptName ,u.department.fullDepartmentName)  from SysUser u");

        StringBuffer where = new StringBuffer();
        where.append("u.isDelete=:isDelete");

        Map<String, Object> params = new HashMap<>();
        params.put("isDelete", Constants.NOT_DEL_VALUE);

        if (Objects.nonNull(condition)) {
            List<QueryItem> queryItems = condition.getQuery();
            if (Objects.nonNull(queryItems) && !queryItems.isEmpty()) {
                for (QueryItem q : condition.getQuery()) {
                    String column = q.getColumn();
                    Object value = q.getValue();
                    String temp = null;
                    if (Objects.nonNull(value)) {
                        if ("username".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                            where.append(" and u.username like :username");
                            params.put("username", "%" + temp + "%");
                        } else if ("phone".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                            where.append(" and u.phone like :phone");
                            params.put("phone", "%" + temp + "%");
                        } else if ("role".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                            hql.append(" join u.roles r");
                            where.append(" and r.id=:role");
                            params.put("role", Long.parseLong(temp));
                        } else if ("department".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                            where.append(" and u.department.id=:department");
                            params.put("department", Long.parseLong(temp));
                        } else if ("id".equals(column) && StringUtils.isNotBlank(temp = value.toString())) {
                            where.append(" and u.id=:id");
                            params.put("id", Long.parseLong(temp));
                        }
                    }
                }
            }
        }
        Query query = this.entityManager.createQuery(hql.append(" where ").append(where).toString());
        params.forEach((key, value) -> query.setParameter(key, value));
        return query.getResultList();
    }

    private void addCommonAddressData(List<AddressBookVO> list) {
        AddressBookVO addressBookVO1 = new AddressBookVO();
        addressBookVO1.setId(0L);
        addressBookVO1.setType("common");
        List<AddressBookUserVO> addressBookUserVOList = new ArrayList<>(5);
        addressBookUserVOList.add(new AddressBookUserVO(1L, "中心综合办公室", "029110110110", serverName + "/image/zgbgs.png"));
        addressBookUserVOList.add(new AddressBookUserVO(2L, "中心培训部", "029110110110", serverName + "/image/pxb.png"));
        addressBookUserVOList.add(new AddressBookUserVO(1L, "中心后勤处", "029110110110", serverName + "/image/hqc.png"));
        addressBookUserVOList.add(new AddressBookUserVO(1L, "中心员工餐厅", "029110110110", serverName + "/image/ygct.png"));
        addressBookUserVOList.add(new AddressBookUserVO(1L, "中心人事部", "029110110110", serverName + "/image/rsb.png"));
        addressBookVO1.setList(addressBookUserVOList);
        list.add(addressBookVO1);
    }

    private void addAddressBookUserVo(SysUserVO sysUser, AddressBookVO t) {
        AddressBookUserVO addressBookUserVO = new AddressBookUserVO();
        addressBookUserVO.setId(sysUser.getId());
        addressBookUserVO.setUsername(sysUser.getUsername());
        addressBookUserVO.setPhone(sysUser.getPhone());
        t.getList().add(addressBookUserVO);
    }

    private void addDepartmentId(List<Long> departmentIds, SysDepartment department) {
        if (Objects.nonNull(department)) {
            departmentIds.add(department.getId());
            List<SysDepartment> children = department.getChildren();
            if (Objects.nonNull(children) && !children.isEmpty()) {
                children.forEach(t -> addDepartmentId(departmentIds, t));
            }
        }
    }

    private void addParentDepartmentId(List<Long> departmentIds, SysDepartment sysDepartment) {
        SysDepartment parent = null;
        if (Objects.nonNull(sysDepartment) && Objects.nonNull(parent = sysDepartment.getParent())) {
            addParentDepartmentId(departmentIds, parent);
            departmentIds.add(parent.getId());
        }
    }

    @Override
    protected void addEntity(SysUser entity) {
        entity.setPassword(passwordEncoder.encode(DEFAULT_PASSWORD));
    }

    @Override
    protected void updateEntity(SysUser oldEntity, SysUser entity) {
        updateSysUserRoles(oldEntity, entity);
    }

    private void updateSysUserRoles(SysUser oldEntity, SysUser entity) {
        Set<SysRole> entityRoles = entity.getRoles();
        oldEntity.setRoles(entityRoles);
//        if (Objects.nonNull(entityRoles) && !entityRoles.isEmpty() && Objects.nonNull(oldRoles) && !oldRoles.isEmpty()) {
//            // 新增角色
//            Set<Long> roleSet = new HashSet<>();
//
//            // 角色去重
//            entityRoles.forEach((data) -> roleSet.add(data.getId()));
//            oldRoles.forEach((data) -> roleSet.add(data.getId()));
//
//            List<SysRole> sysRoles = this.changeIdsToSysRoles(roleSet);
//            oldEntity.setRoles(sysRoles);
//        }
    }

    private Set<SysRole> changeIdsToSysRoles(Set<Long> difference) {
        Set<SysRole> result = new HashSet<>();
        difference.forEach((id) -> {
            SysRole sysRole = new SysRole();
            sysRole.setId(id);
            result.add(sysRole);
        });
        return result;
    }
}
